#  Copyright 2007, Benjamin Feng
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are met:
#
#   1. Redistributions of source code must retain the above copyright notice,
#      this list of conditions and the following disclaimer.
#   2. Redistributions in binary form must reproduce the above copyright
#      notice, this list of conditions and the following disclaimer in the
#      documentation and/or other materials provided with the distribution.
#   3. The name of the author may not be used to endorse or promote products
#      derived from this software without specific prior written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
#  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
#  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
#  EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
#  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
#  OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
#  WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
#  OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
#  ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


"""
Command-line Usage
=====

"makepyfile.py" is standard filename for a make.py task list. When invoked with
no arguments, it runs the default task. Otherwise, it will run the selected
task(s).

  $ make.py
  $ make.py clean


Library
=======

Most of the make.py's functionality is embedded into the top-level module.
Note: make.py aims for a Pythonic implementation. While "from makepy import *"
may work, it is *not* recommended.

  >>> import makepy


Basic Task
----------

A task will assume the target of the function it is wrapping. The simplest way
to declare a task is by using the decorator @makepy.task.

  >>> @makepy.task
  ... def camelot():
  ...     print 'Inside camelot'
  ...     return 'castle'
  ...
  >>> camelot.target
  'camelot'

The task may be invoked by the object directly.

  >>> camelot.run()
  Inside camelot
  'castle'

However, the task will only run once and caches the return value. Subsequent
calls do not invoke the function but still returns the previous call value.

  >>> camelot.run()
  'castle'

The task may still be forced to invoke the function. This will also override the
cache.

  >>> camelot.run(force=True)
  Inside camelot
  'castle'

The task may also be invoked with make.py directly. This requires an absolute
task path.

A task can also have arguments. However, only the no-argument call will be used
as a task; any arguments will cause the task to behave like a simple function.

  >>> @makepy.task
  ... def agh(*args):
  ...     print 'Inside agh'
  ...     return ' '.join(args + (camelot.run(),))
  ...
  >>> agh.run()
  Inside agh
  'castle'
  >>> agh.run()
  'castle'
  >>> agh()
  Inside agh
  'castle'
  >>> agh()
  Inside agh
  'castle'
  >>> agh('French')
  Inside agh
  'French castle'
  >>> agh('French')
  Inside agh
  'French castle'

The docstring of the function will be the description.

  >>> @makepy.task
  ... def anthrax():
  ...     '''lit beacon'''
  ...     pass
  ...
  >>> anthrax.descr
  'lit beacon'

While a dependency may be invoked directly within the function body, it may be
clearer to define it on the task itself.
Note: All arguments to makepy.task should be referenced by keyword for clarity.

  >>> @makepy.task
  ... def ni():
  ...     print 'ni'
  ...
  >>> @makepy.task(depends=['ni'])
  ... def nu():
  ...     print 'nu'
  ...
  >>> nu.run()
  ni
  nu

A task can also be a different target from the function. However, only the task
list call is changed; the object itself will still have the same call signature
as before.
Note: Task targets may be positional. Yes, I lied previously. Don't use
positional for anything else. And this time I mean it.

  >>> @makepy.task('european_swallow')
  ... def swallow():
  ...     return 'coconut'
  ...
  >>> swallow.run()
  'coconut'
  >>> european_swallow.run()
  Traceback (most recent call last):
      ...
  NameError: name 'european_swallow' is not defined
  >>> @makepy.task(depends=['swallow'])
  ... def unladen():
  ...     pass
  ...
  >>> unladen.run()
  Traceback (most recent call last):
      ...
  TargetNotFoundException: makepy/swallow
  >>> @makepy.task(depends=['european_swallow'])
  ... def unladen():
  ...     pass
  ...
  >>> unladen.run()


File Task
---------

A filetask is a special type of task. When called, it compares the target
file with its dependent files. The task will always run if the target file does
not exist.

NOTE: These are currently raw *nix calls. Will add real functions soon.

  >>> @makepy.filetask('shrubbery.c')
  ... def make_shrubbery():
  ...     f = open('shrubbery.c', 'w')
  ...     f.write('''
  ...         #include <stdio.h>
  ...         int main(int argc, char *argv[]) {
  ...             printf(\\"A herring!\\");
  ...         }
  ...     ''')
  ...     f.close()
  ...
  >>> @makepy.filetask(depends=['shrubbery.c'])
  ... def shrubbery():
  ...     makepy.sh('gcc -o shrubbery shrubbery.c')
  ...     execute_shrubbery.run()
  ...
  >>> @makepy.task
  ... def execute_shrubbery():
  ...     print makepy.sh('./shrubbery')
  ...
  >>> @makepy.task
  ... def clean_shrubbery():
  ...     makepy.sh('rm shrubbery*')
  ...
  >>> shrubbery.run()
  A herring!
  >>> clean_shrubbery.run()
"""


import sys

from . import core
from .commands import sh


task = core.Task.decorator
filetask = core.Filetask.decorator
